package simpledb.optimizer;

import simpledb.execution.Predicate;

/**
 * StringHistogram 类：表示一个基于字符串字段的固定宽度直方图。
 *
 * 该类用于统计数据库中某一字符串字段的分布情况，并估计某个查询条件的选择率（selectivity）。
 * 实际上是通过将字符串转换为整数，使用前面实现的 IntHistogram 来完成统计和选择率估算。
 */
public class StringHistogram implements Histogram<String> {

    // 使用 IntHistogram 实现字符串直方图
    final IntHistogram hist;

    /**
     * 创建一个新的 StringHistogram。
     *
     * @param buckets 要划分的桶的数量
     */
    public StringHistogram(int buckets) {
        hist = new IntHistogram(buckets, minVal(), maxVal());
    }

    /**
     * 将字符串转换为整数，且保证：
     * 如果 s1 < s2，则 stringToInt(s1) < stringToInt(s2)
     *
     * 这里我们取字符串前4个字符，按ASCII码拼接成一个int值。
     * 若字符串长度不足4位，用0填充高位。
     *
     * @param s 待转换的字符串
     * @return 对应的整数值
     */
    private int stringToInt(String s) {
        int v = 0;
        for (int i = 3; i >= 0; i--) {
            if (s.length() > 3 - i) {
                int ci = s.charAt(3 - i);
                v += (ci) << (i * 8); // 每个字符左移对应字节位置，组成int
            }
        }

        // 防止超出最小值或最大值范围
        if (!(s.equals("") || s.equals("zzzz"))) {
            if (v < minVal()) {
                v = minVal();
            }
            if (v > maxVal()) {
                v = maxVal();
            }
        }

        return v;
    }

    /**
     * 获取字符串字段的最大可能对应的整数值。
     * 我们假设最大字符串为 "zzzz"
     */
    int maxVal() {
        return stringToInt("zzzz");
    }

    /**
     * 获取字符串字段的最小可能对应的整数值。
     * 我们假设最小字符串为空字符串 ""
     */
    int minVal() {
        return stringToInt("");
    }

    /**
     * 添加一个字符串值到直方图中。
     *
     * 内部将其转换为整数后调用 IntHistogram 的 addValue 方法。
     *
     * @param s 要添加的字符串
     */
    public void addValue(String s) {
        int val = stringToInt(s);
        hist.addValue(val);
    }

    /**
     * 根据当前直方图估计某个谓词和字符串值的选择率。
     *
     * 例如：如果 op 是 EQUALS 且 s 是 "abc"，
     * 则返回等于 "abc" 的元组占总元组数的比例。
     *
     * @param op 操作符（如 EQUALS、GREATER_THAN 等）
     * @param s 查询字符串
     * @return 估计的选择率（0.0 ~ 1.0）
     */
    public double estimateSelectivity(Predicate.Op op, String s) {
        int val = stringToInt(s);
        return hist.estimateSelectivity(op, val);
    }

    /**
     * 获取该直方图的平均选择率。
     *
     * 这个方法不是实现基本连接优化所必需的，
     * 但在实现更高效的优化策略时可能会用到。
     *
     * @return 平均选择率
     */
    public double avgSelectivity() {
        return hist.avgSelectivity();
    }
}
